package b.sync.domain

import zio._

object ZioReplicator {

  type ZioReplicator = Has[Service]

  trait Service {
    def replicateOne[W](rowsGroup: (Int, List[(W, Int)])): Task[Int]
    def copyTo[R, W](row: R, opType: Int): UIO[(W, Int)]
    def uniqueKeyOf[W, K](row: W): UIO[K]
    def replicate[R, W](rows: List[(R, Int, Int)]): Task[Int] = for {
      copies <- ZIO.foreachPar(rows) { case (data, no, op) =>
        for (copy <- copyTo[R, W](data, op)) yield (copy._1, no, copy._2)
      }
      grouped = copies.foldLeft(List.empty[(Int, List[(W, Int)])]) { case (grouping, (data, no, op)) =>
        grouping.lastOption match {
          case Some((`op`, list)) => grouping.dropRight(1) :+ (op, list :+ (data, no))
          case _ => grouping :+ (op, List((data, no)))
        }
      }
      all <- ZIO.foldLeft(grouped)(0) { (count, group) =>
        for (b <- replicateOne(group)) yield count + b
      }
    } yield all
  }

  def replicate[R, W](rows: List[(R, Int, Int)]): RIO[ZioReplicator, Int] =
    ZIO.accessM(_.get.replicate(rows))
  def replicateOne[W](rowsGroup: (Int, List[(W, Int)])): RIO[ZioReplicator, Int] =
    ZIO.accessM(_.get.replicateOne(rowsGroup))
  def copyTo[R, W](row: R, opType: Int): RIO[ZioReplicator, (W, Int)] =
    ZIO.accessM(_.get.copyTo(row, opType))
  def uniqueKeyOf[W, K](row: W): RIO[ZioReplicator, K] =
    ZIO.accessM(_.get.uniqueKeyOf(row))
}
